Skip to content

feat: revamp of session view#1618

Merged
olzhik11 merged 51 commits intodevfrom
feat/revamp-session-view
Apr 10, 2026
Merged

feat: revamp of session view#1618
olzhik11 merged 51 commits intodevfrom
feat/revamp-session-view

Conversation

@olzhik11
Copy link
Copy Markdown
Member

@olzhik11 olzhik11 commented Apr 9, 2026

Note

Medium Risk
Introduces new session UI/data flows (virtualized rendering, sorting, trace expansion) and a new backend/Next API for batched trace IO extraction with caching and LLM-generated regexes, which could affect performance and correctness of displayed data.

Overview
Revamps the Sessions view to use a new virtualized list with expandable session rows, sortable columns, and an AdvancedSearch-based query UX (including safer filter submission and a longer default time range).

Adds a new POST /api/projects/:projectId/traces/io Next.js route and supporting server actions to fetch batched trace input/output previews; inputs are parsed from span message formats, grouped by a structural system-prompt hash, and optionally extracted via an LLM-generated regex with Redis/in-memory caching.

Backend adds a new POST /api/v1/projects/:project_id/skeleton-hashes endpoint to compute structural skeleton hashes, and sessions queries now support server-side sorting via sortColumn/sortDirection.

Reviewed by Cursor Bugbot for commit bd0713c. Bugbot is set up for automated code reviews on this repo. Configure here.

kolbeyang and others added 30 commits March 31, 2026 10:56
Add a lightweight API endpoint to fetch trace timeline data (id, startTime,
endTime, status) grouped by session ID. This supports rendering timeline
bars in the new session view without loading full trace data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Build 6 presentational components for the new session table: header,
session row with chevron/time/ID/totals/timeline, totals pill, traces
timeline with proportional bars, trace section header, and trace card
with placeholder input/output columns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace InfiniteDataTable with @tanstack/react-virtual virtualizer for
sessions view. Implements flat list computation (session rows, trace
section headers, trace cards), expand/collapse with trace fetching,
timeline data fetching, IntersectionObserver infinite scroll, and trace
card click navigation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… fixes

Add loading/empty states for session expansion, initial session loading,
and empty results. Handle edge cases: null-safe cost/token rendering,
timeline minimum bar widths, back-to-back trace gap filtering, sticky
header, gradient fade color fix, and title tooltips for truncated IDs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…, row click)

Reset expanded/trace/timeline state when query params change to prevent
stale data. Add stable getItemKey to virtualizer for smooth expand/collapse.
Wire session row click to toggle expand matching cursor-pointer affordance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove /1000 division in formatDuration since SessionRow.duration is already in seconds
- Change input/output columns from overflow-clip to overflow-y-auto for scrollability

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pand animations

Fix gradient overlays to use absolute positioning without sticky conflict,
add pointer-events-none so gradients don't block scrolling. Extend placeholder
text to 3+ paragraphs to demonstrate scrolling within 140px cards. Update
estimateSize to match the new 140px card height. Add framer-motion fade-in
animations (opacity + scale spring) for trace cards, section headers, and
loading/empty states on session expand.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… defaults

Merge timeline fetching into the sessions endpoint to eliminate a second
network call. Extract session expansion state into a dedicated Zustand
store following existing codebase patterns. Change default time range
from 24h to 72h and remove incorrect .toReversed() on already-descending
trace results.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Change gradient overlays from absolute to sticky positioning so they
stay anchored at the bottom of the scrollable viewport instead of
scrolling away with content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wrap virtualizer items with AnimatePresence and add exit props
(opacity fade + slight scale) to motion.div wrappers so collapsing
a session provides a smooth visual transition instead of instant
disappearance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix keyboard navigation including collapsed session traces by filtering
  allVisibleTraceIds to only expanded sessions in rendered order
- Add LIMIT 1000 to timeline query to prevent unbounded row fetches
- Validate pastHours with parseFloat check and use UInt32 parameter type
  matching the pattern in query-builder.ts
- Surface fetch errors in SessionsVirtualList with retry option

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Prevent re-expanding a session while its trace fetch is in-flight
- Clear expanded state when refresh button is clicked
- Increase timeline query LIMIT from 1000 to 5000 for multi-session pages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
setSessionTraces now checks if the session is still expanded before
writing. Prevents stale in-flight responses from repopulating traces
after resetExpandState clears state on filter/date changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace overlapping concurrency guards (loadingSessions check,
expandedSessions stale-write guard) with AbortController pattern.
Controllers live in closure scope inside createSessionsStore, keeping
non-serializable objects out of Zustand state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…elines

- Remove unused abortSession/abortAllSessions from store (abort logic
  is already inline in collapseSession/resetExpandState)
- Add fetchVersionRef to discard stale timeline data when params change
  mid-flight

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Timeline bars are now clickable — opens trace side panel
- Fix gradient overlay scrolling with content by wrapping text in
  scrollable inner div, keeping gradient absolute in outer container
- Animate expanded items with height 0→auto instead of scale
- Simplify onTraceClick to take traceId string instead of TraceRow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Timeline bars are clickable — opens trace side panel
- Session ID and trace card ID click-to-copy via CopyTooltip
- Fix gradient overlay scrolling (inner scroll wrapper)
- Unified layout: header always mounted, states swap underneath
- Height accordion animation on expand, no exit animation
- Simplified onTraceClick to take traceId string

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lick race

toggleSessionExpanded was symmetric — double-clicking before React
re-rendered would undo the expand. Replaced with expandSession (add-only)
and collapseSession (remove-only) for directional intent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Query ClickHouse for the main agent's first and last LLM spans
(grouped by system prompt hash) to extract user input and assistant
output. Render both columns with the shared Markdown component.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The first LLM span's last user message is the actual task input,
not the first user message which may be a system-level prompt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… ASC

- Consolidate wrapper div, gradient, and expand button into TraceIOContent
- Expand button toggles collapse/expand (shared state between input/output)
- Chevron icon (ChevronDown/Up) replaces "Expand" text, shows on group-hover
- Gradient always visible; chevron opacity controlled by group-hover
- Sort trace cards within a session ascending by startTime
- Fix extractLastUserMessage to use findLast, skipping injected system-reminders

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sessions store

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ct fetch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…) for trace ID validation

ClickHouse-generated trace IDs are valid 8-4-4-4-12 hex UUIDs but do not
conform to RFC 4122 version/variant constraints, causing z.string().uuid()
to reject every real trace ID with HTTP 400. Replace with a regex that
accepts any well-formed hex UUID regardless of version or variant nibble.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… guard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… single source of truth

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for all 3 issues found in the latest run.

  • ✅ Fixed: Exported TraceTimelineItem type is unused dead code
    • Removed the unused exported TraceTimelineItem type from the traces types module.
  • ✅ Fixed: Stale closure causes session toggle to malfunction
    • Added a store-level isSessionExpanded getter and used it in the toggle handler so expansion state is read from current store state instead of a stale closure.
  • ✅ Fixed: Missing shallow equality causes excessive re-renders
    • Updated the actions selector useSessionsStoreContext call to use shallow, preventing unnecessary re-renders from new object literals.

Create PR

Or push these changes by commenting:

@cursor push 30ffabbb6e
Preview (30ffabbb6e)
diff --git a/frontend/components/traces/sessions-table/index.tsx b/frontend/components/traces/sessions-table/index.tsx
--- a/frontend/components/traces/sessions-table/index.tsx
+++ b/frontend/components/traces/sessions-table/index.tsx
@@ -63,6 +63,7 @@
 
   const {
     expandSession,
+    isSessionExpanded,
     collapseSession,
     setLoadingSession,
     setSessionTraces,
@@ -70,16 +71,20 @@
     setLoadingSessionIO,
     resetExpandState,
     getController,
-  } = useSessionsStoreContext((state) => ({
-    expandSession: state.expandSession,
-    collapseSession: state.collapseSession,
-    setLoadingSession: state.setLoadingSession,
-    setSessionTraces: state.setSessionTraces,
-    mergeTraceIO: state.mergeTraceIO,
-    setLoadingSessionIO: state.setLoadingSessionIO,
-    resetExpandState: state.resetExpandState,
-    getController: state.getController,
-  }));
+  } = useSessionsStoreContext(
+    (state) => ({
+      expandSession: state.expandSession,
+      isSessionExpanded: state.isSessionExpanded,
+      collapseSession: state.collapseSession,
+      setLoadingSession: state.setLoadingSession,
+      setSessionTraces: state.setSessionTraces,
+      mergeTraceIO: state.mergeTraceIO,
+      setLoadingSessionIO: state.setLoadingSessionIO,
+      resetExpandState: state.resetExpandState,
+      getController: state.getController,
+    }),
+    shallow
+  );
 
   // Serialize filter array for stable dependency comparison
   const filterKey = JSON.stringify(filter);
@@ -157,7 +162,7 @@
 
   const handleToggleSession = useCallback(
     async (sessionId: string) => {
-      const isExpanded = expandedSessions.has(sessionId);
+      const isExpanded = isSessionExpanded(sessionId);
 
       if (isExpanded) {
         collapseSession(sessionId);
@@ -226,13 +231,13 @@
       }
     },
     [
-      expandedSessions,
       pastHours,
       startDate,
       endDate,
       projectId,
       toast,
       expandSession,
+      isSessionExpanded,
       setLoadingSession,
       setSessionTraces,
       mergeTraceIO,

diff --git a/frontend/components/traces/sessions-table/sessions-store.tsx b/frontend/components/traces/sessions-table/sessions-store.tsx
--- a/frontend/components/traces/sessions-table/sessions-store.tsx
+++ b/frontend/components/traces/sessions-table/sessions-store.tsx
@@ -15,6 +15,7 @@
 };
 
 export type SessionsActions = {
+  isSessionExpanded: (sessionId: string) => boolean;
   expandSession: (sessionId: string) => void;
   collapseSession: (sessionId: string) => void;
   setLoadingSession: (sessionId: string, loading: boolean) => void;
@@ -40,9 +41,11 @@
 export const createSessionsStore = () => {
   const sessionControllers = new Map<string, AbortController>();
 
-  return createStore<SessionsStore>()((set) => ({
+  return createStore<SessionsStore>()((set, get) => ({
     ...DEFAULT_STATE,
 
+    isSessionExpanded: (sessionId) => get().expandedSessions.has(sessionId),
+
     expandSession: (sessionId) =>
       set((state) => {
         const next = new Set(state.expandedSessions);

diff --git a/frontend/lib/traces/types.ts b/frontend/lib/traces/types.ts
--- a/frontend/lib/traces/types.ts
+++ b/frontend/lib/traces/types.ts
@@ -191,14 +191,6 @@
   outputMessageIds: string[];
 };
 
-export type TraceTimelineItem = {
-  id: string;
-  name?: string;
-  startTime: string;
-  endTime: string;
-  status: string;
-};
-
 // We have id and sessionId here because
 // its not possible to make good type intersection,
 // and use it in tanstack table wrappers.

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 9, 2026

Tip:

Greploop — Automatically fix all review issues by running /greploops in Claude Code. It iterates: fix, push, re-review, repeat until 5/5 confidence.

Use the Greptile plugin for Claude Code to query reviews, search comments, and manage custom context directly from your terminal.

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bd0713c. Configure here.

@olzhik11 olzhik11 merged commit 8232e3d into dev Apr 10, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants